﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

using BF = System.Reflection.BindingFlags;
using System.Runtime.InteropServices;

namespace ImageRecognition2
{
    /// <summary>
    /// 
    /// </summary>
    public static class UnsafeHelper
    {
        /// <summary>
        /// Gets the PTR.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="arg">The arg.</param>
        /// <returns></returns>
        public static unsafe void* GetPtr<T>(ref T arg) where T : struct
        {
            return UnsafeHelper<T>.GetPtr(ref arg);
        }

        /// <summary>
        /// Structures to PTR.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="dest">The dest.</param>
        public static unsafe void StructureToPtr<T>(ref T source, void* dest) where T : struct
        {
            MoveMemory(dest, UnsafeHelper<T>.GetPtr(ref source), UnsafeHelper<T>.Size);
        }

        /// <summary>
        /// Structures to PTR.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="dest">The dest.</param>
        public static unsafe void StructureToPtr<T>(ref T source, byte[] dest) where T : struct
        {
            MoveMemory(dest, UnsafeHelper<T>.GetPtr(ref source), UnsafeHelper<T>.Size);
        }

        /// <summary>
        /// Structures to PTR.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="dest">The dest.</param>
        /// <param name="destOffset">The dest offset.</param>
        public static unsafe void StructureToPtr<T>(ref T source, byte[] dest, int destOffset) where T : struct
        {
            fixed (byte* pdest = &dest[0]) MoveMemory(pdest + destOffset, UnsafeHelper<T>.GetPtr(ref source), UnsafeHelper<T>.Size);
        }

        /// <summary>
        /// PTRs to structure.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="dest">The dest.</param>
        public static unsafe void PtrToStructure<T>(void* source, ref T dest) where T : struct
        {
            MoveMemory(UnsafeHelper<T>.GetPtr(ref dest), source, UnsafeHelper<T>.Size);
        }

        /// <summary>
        /// PTRs to structure.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="dest">The dest.</param>
        public static unsafe void PtrToStructure<T>(byte[] source, ref T dest) where T : struct
        {
            MoveMemory(UnsafeHelper<T>.GetPtr(ref dest), source, UnsafeHelper<T>.Size);
        }

        /// <summary>
        /// PTRs to structure.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="sourceOffset">The source offset.</param>
        /// <param name="dest">The dest.</param>
        public static unsafe void PtrToStructure<T>(byte[] source, int sourceOffset, ref T dest) where T : struct
        {
            fixed (byte* psource = &source[0]) MoveMemory(UnsafeHelper<T>.GetPtr(ref dest), psource + sourceOffset, UnsafeHelper<T>.Size);
        }

        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static unsafe extern void MoveMemory(void* dest, void* src, int size);
        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static unsafe extern void MoveMemory(byte[] dest, void* src, int size);
        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        public static unsafe extern void MoveMemory(void* dest, byte[] src, int size);

        /// <remarks>Schneller bei mehrmaligem Aufruf! Anders als bei Marshal.SizeOf wird char mit 2 bytes übersetzt!</remarks>
        /// <see cref="UnsafeHelper<T>.Size"/>
        public static int SizeOf<T>()
        {
            return UnsafeHelper<T>.Size;
        }

        /// <summary>
        /// Sizes the of.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        /// <remarks>Langsamer bei mehrmaligem Aufruf. Anders als bei Marshal.SizeOf wird char mit 2 bytes übersetzt!</remarks>
        public static int SizeOf(Type type)
        {
            object[] attrs = type.GetCustomAttributes(typeof(StructLayoutAttribute), false);
            if (attrs.Length > 0)
            {
                StructLayoutAttribute attr = (StructLayoutAttribute)attrs[0];
                if (attr.Size > 0) return attr.Size;
            }

            if (type.IsPrimitive)
            {
                if (type == typeof(IntPtr))
                    return IntPtr.Size;

                if (type == typeof(double) ||
                    type == typeof(long) ||
                    type == typeof(ulong))
                    return 8;

                if (type == typeof(float) ||
                    type == typeof(int) ||
                    type == typeof(uint))
                    return 4;

                if (type == typeof(char) ||
                    type == typeof(short) ||
                    type == typeof(ushort))
                    return 2;

                if (type == typeof(bool) ||
                    type == typeof(sbyte) ||
                    type == typeof(byte))
                    return 1;
            }
            else if (type.IsPointer)
            {
                return IntPtr.Size;
            }
            else if (type.IsValueType)
            {
                int sum = 0;
                foreach (FieldInfo info in type.GetFields(BF.Instance | BF.Public | BF.NonPublic))
                {
                    sum += SizeOf(info.FieldType);
                }
                return sum;
            }
            throw new NotSupportedException(type.FullName);
        }
    }

    public static class UnsafeHelper<T>
    {
        public unsafe delegate void* getPtrFunc(ref T value);
        public unsafe readonly static getPtrFunc GetPtr = BuildFunction();
        public static readonly int Size = UnsafeHelper.SizeOf(typeof(T));
        private static DynamicMethod method;

        /// <summary>
        /// Builds the function.
        /// </summary>
        /// <returns></returns>
        private unsafe static getPtrFunc BuildFunction()
        {
            method = new DynamicMethod("getPtr<" + typeof(T).FullName.Replace(".", "<>") + ">",
                typeof(void*), new Type[1] { typeof(T).MakeByRefType() }, typeof(UnsafeHelper).Module);

            ILGenerator generator = method.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Conv_U);
            generator.Emit(OpCodes.Ret);
            return (getPtrFunc)method.CreateDelegate(typeof(getPtrFunc));
        }
    }
}
